home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / pcgen.asm < prev    next >
Encoding:
Assembly Source File  |  1993-04-08  |  21.9 KB  |  857 lines

  1. ; Collection of assembler support routines for NOS
  2. ; Copyright 1991 Phil Karn, KA9Q
  3.  
  4. ;%    .MODEL  MEMMOD,C
  5. include asmglobal.h        
  6.     LOCALS
  7.     %MACS
  8.     .LALL
  9.     extrn   ctick:proc
  10.     public  eoi
  11.  
  12. ; Hardware vector for timer linkage
  13. ; We use the timer hardware channel here instead of the indirect BIOS
  14. ; channel (1ch) because the latter is sluggish when running under DoubleDos
  15. TIMEVEC EQU     08h
  16.  
  17.     .DATA
  18.     public  Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab
  19.     extrn   Isat:word
  20. Spsave  dw      ?               ; Save location for SP during interrupts
  21. Sssave  dw      ?               ; Save location for SS during interrupts
  22. Intstk  dw      512 dup(?)      ; Interrupt working stack
  23. Stktop  equ     $               ; SP set here when entering interrupt
  24. Mtasker db      ?               ; Type of higher multitasker, if any
  25. Hashtab db      256 dup(?)      ; Modulus lookup table for iphash()
  26.     .CODE
  27. dbase   dw      @Data
  28. jtable  dw      l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15   
  29.  
  30. ; Re-arm 8259 interrupt controller(s)
  31. ; Should be called just after taking an interrupt, instead of just
  32. ; before returning. This is because the 8259 inputs are edge triggered, and
  33. ; new interrupts arriving during an interrupt service routine might be missed.
  34. eoi     proc
  35.     cmp     Isat,1
  36.     jnz     @@1             ; Only one 8259, so skip this stuff
  37.     mov     al,0bh          ; read in-service register from
  38.     out     0a0h,al         ; secondary 8259
  39.     nop                     ; settling delay
  40.     nop
  41.     nop
  42.     in      al,0a0h         ; get it
  43.     or      al,al           ; Any bits set?
  44.     jz      @@1             ; nope, not a secondary interrupt
  45.     mov     al,20h          ; Get EOI instruction
  46.     out     0a0h,al         ; Secondary 8259 (PC/AT only)
  47. @@1:    mov     al,20h          ; 8259 end-of-interrupt command
  48.     out     20h,al          ; Primary 8259
  49.     ret
  50. eoi     endp
  51.  
  52. ; common routine for interrupt return
  53.     public  doret
  54.     label   doret   far
  55.     pop     es
  56.     POPALL
  57.       ;  pop     di
  58.       ;  pop     si
  59.       ;  pop     bp
  60.       ;  pop     dx
  61.       ;  pop     cx
  62.       ;  pop     bx
  63.       ;  pop     ax
  64.     mov     ss,Sssave
  65.     mov     sp,Spsave       ; restore original stack context
  66.     pop     ds
  67.     iret
  68.  
  69. ; istate - return current interrupt state
  70.     public  istate
  71. istate  proc
  72.     pushf
  73.     pop     ax
  74.     and     ax,200h
  75.     jnz     @@1
  76.     ret
  77. @@1:    mov     ax,1
  78.     ret
  79. istate  endp
  80.  
  81. ; dirps - disable interrupts and return previous state: 0 = disabled,
  82. ;       1 = enabled
  83.     public dirps
  84. dirps   proc
  85.     pushf                   ; save flags on stack
  86.     pop     ax              ; flags -> ax
  87.     and     ax,200h         ; 1<<9 is IF bit
  88.     jz      @@1             ; ints are already off; return 0
  89.     mov     ax,1
  90.     cli                     ; interrupts now off
  91. @@1:    ret
  92. dirps   endp
  93.  
  94. ; restore - restore interrupt state: 0 = off, nonzero = on
  95.     public  restore
  96. restore proc
  97.     arg is:word
  98.     test    is,0ffffh
  99.     jz      @@1
  100.     sti
  101.     ret
  102. @@1:    cli     ; should already be off, but just in case...
  103.     ret
  104. restore endp
  105.  
  106. ; multitasker types
  107. NONE            equ     0
  108. DOUBLEDOS       equ     1
  109. DESQVIEW        equ     2
  110. WINDOWS3        equ     3
  111. DOS5STYLE       equ     4
  112. OS2STYLE        equ     5
  113. DPMISTYLE       equ     6
  114.  
  115. ; Relinquish processor so other task can run
  116.     public  giveup
  117. giveup  proc
  118.     pushf           ;save caller's interrupt state
  119.     sti             ;re-enable interrupts
  120.     cmp     mtasker, DOUBLEDOS
  121.     jnz     @@1
  122.     mov     al,2    ; 110 ms
  123.     mov     ah,0eeh
  124.     int     21h
  125.     popf            ; restore caller's interrupt state
  126.     ret
  127.  
  128. @@1:    cmp     mtasker, DESQVIEW
  129.     jnz     @@2
  130.     mov     ax, 1000h
  131.     int     15h
  132.     popf            ; restore interrupts
  133.     ret
  134.  
  135. @@2:    cmp     mtasker, WINDOWS3
  136.     je      @@4
  137.     cmp     mtasker, OS2STYLE
  138.     je      @@4
  139.     cmp     mtasker, DOS5STYLE
  140.     je      @@4
  141.     cmp     mtasker, DPMISTYLE
  142.     jne     @@3
  143. @@4:    mov     ax, 1680h       ; Release time for DOS 5, OS/2 VDM, Win 3.x
  144.     int     2fh
  145.     cmp     al, 80h ; call supported?
  146.     jz      @@3     ; nope
  147.     popf            ; yes - restore interrupts
  148.     ret
  149.  
  150. @@3:    hlt             ; wait for an interrupt
  151.     popf            ; restore interrupts
  152.     ret
  153. giveup  endp
  154.  
  155. ; check for a multitasker running
  156.     public  chktasker
  157. chktasker       proc
  158.     mov     mtasker,NONE
  159.  
  160.     ; Check for Microsoft Windows
  161.     mov     ax,1600h
  162.     int     2fh
  163.     test    al,7fh          ; 00h or 80h means Windows not multitasking
  164.     jz      @@4
  165.     mov     mtasker, WINDOWS3
  166.     ret
  167.  
  168.     ; Check for DoubleDos
  169. @@4:    mov     ah,0e4h
  170.     int     21h
  171.     cmp     al,1
  172.     jz      @@1
  173.     cmp     al,2
  174.     jnz     @@2
  175. @@1:    mov     mtasker, DOUBLEDOS
  176.     ret
  177.  
  178.     ; Check for DESQVIEW
  179. @@2:    mov     ax, 2b01h
  180.     mov     cx, 4445h
  181.     mov     dx, 5351h
  182.     int     21h
  183.     cmp     al, 0ffh
  184.     jz      @@3
  185.     mov     mtasker, DESQVIEW
  186.     ret
  187.  
  188.     ; Check for DOS 5.0 or OS/2 2.0 (N1BEE)
  189. @@3:    mov     ax, 3306h       ;Get true DOS version
  190.     xor     bx,bx
  191.     int     21h
  192.     cmp     bl, 20          ;DOS 20.x (OS/2 2.0 DOS emulation)
  193.     jne     @@5
  194.     mov     mtasker, OS2STYLE
  195.     ret
  196.  
  197. @@5:    cmp     bl, 5           ;DOS 5.x
  198.     je     @@5A
  199.     cmp     bl, 6               ;DOS 6.x
  200.     jne     @@6
  201. @@5A:
  202.     mov     mtasker, DOS5STYLE      ;Release time same way as Win 3.x
  203.     ret
  204.     
  205.     ; Handle OS/2 Virtual Boot Machine (N1BEE)
  206. @@6:    mov     ax, 1687h       ;Check DPMI installed state
  207.     int     2fh
  208.     or      ax,ax   
  209.     jnz     @@7
  210.     mov     mtasker, DPMISTYLE
  211. @@7:    ret
  212.  
  213. chktasker       endp
  214.  
  215.  
  216.  
  217. ; getss - Read SS for debugging purposes
  218.     public  getss
  219. getss   proc
  220.     mov     ax,ss
  221.     ret
  222. getss   endp
  223.  
  224. ; clockbits - Read low order bits of timer 0 (the TOD clock)
  225. ; This works only for the 8254 chips used in ATs and 386s.
  226. ;
  227. ; The timer runs in mode 3 (square wave mode), counting down
  228. ; by twos, twice for each cycle. So it is necessary to read back the
  229. ; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
  230. ; pin forms the most significant bit of the count. Unfortunately,
  231. ; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
  232. ;
  233. ; The PC's clock design is soooo brain damaged...
  234.  
  235.     public  clockbits
  236. clockbits       proc
  237.     mov     al,0c2h ; latch timer 0 count and status for reading
  238.     pushf
  239.     cli             ; make chip references atomic
  240.     out     43h,al  ; send latch command
  241.     in      al,40h  ; get status of timer 0
  242.     mov     bl,al   ; save status
  243.     in      al,40h  ; get lsb of count
  244.     mov     ah,al   ; save lsb
  245.     in      al,40h  ; get msb of count
  246.     popf            ; no more chip references
  247.     and     bl,80h  ; we're only interested in the OUT bit
  248.     xchg    ah,al   ; ax = count in correct order
  249.     shr     ax,1    ; count /= 2
  250.     jz      @@3     ; zero count requires carry propagation
  251. @@2:    or      ah,bl   ; combine with OUT bit as most sig bit of count
  252.     ret
  253. @@3:    xor     bl,80h  ; propagate carry by toggling OUT bit when cnt == 0
  254.     or      ah,bl   ; combine with !OUT bit as most sig bit of count
  255.     ret
  256.  
  257. clockbits       endp
  258.  
  259. ; Internet checksum subroutine
  260. ; Compute 1's-complement sum of data buffer
  261. ; Uses an unwound loop inspired by "Duff's Device" for performance
  262. ;
  263. ; Called from C as
  264. ; unsigned short
  265. ; lcsum(buf,cnt)
  266. ; unsigned short *buf;
  267. ; unsigned short cnt;
  268.     public  lcsum
  269. lcsum   proc
  270.     arg     buf:ptr,cnt:word
  271.  
  272.     if      @Datasize NE 0
  273.         uses    ds,si
  274.         lds     si,buf  ; ds:si = buf
  275.     else
  276.         uses    si
  277.         mov     si,buf  ; ds:si = buf (ds already set)
  278.     endif
  279.  
  280.     mov     cx,cnt          ; cx = cnt
  281.     cld                     ; autoincrement si
  282.     mov     ax,cx
  283.     shr     cx,1            ; cx /= 16, number of loop iterations
  284.     shr     cx,1
  285.     shr     cx,1
  286.     shr     cx,1
  287.  
  288.     inc     cx              ; make fencepost adjustment for 1st pass
  289.     and     ax,15           ; ax = number of words modulo 16
  290.     shl     ax,1            ; *=2 for word table index
  291.     lea     bx,jtable       ; bx -> branch table
  292.     add     bx,ax           ; index into jump table
  293.     clc                     ; initialize carry = 0
  294.     mov     dx,0            ; clear accumulated sum
  295.     jmp     word ptr cs:[bx]        ; jump into loop
  296.  
  297. ; Here the real work gets done. The numeric labels on the lodsw instructions
  298. ; are the targets for the indirect jump we just made.
  299. ;
  300. ; Each label corresponds to a possible remainder of (count / 16), while
  301. ; the number of times around the loop is determined by the quotient.
  302. ;
  303. ; The loop iteration count in cx has been incremented by one to adjust for
  304. ; the first pass.
  305. deloop: lodsw
  306.     adc     dx,ax
  307. l15:    lodsw
  308.     adc     dx,ax
  309. l14:    lodsw
  310.     adc     dx,ax
  311. l13:    lodsw
  312.     adc     dx,ax
  313. l12:    lodsw
  314.     adc     dx,ax
  315. l11:    lodsw
  316.     adc     dx,ax
  317. l10:    lodsw
  318.     adc     dx,ax
  319. l9:     lodsw
  320.     adc     dx,ax
  321. l8:     lodsw
  322.     adc     dx,ax
  323. l7:     lodsw
  324.     adc     dx,ax
  325. l6:     lodsw
  326.     adc     dx,ax
  327. l5:     lodsw
  328.     adc     dx,ax
  329. l4:     lodsw
  330.     adc     dx,ax
  331. l3:     lodsw
  332.     adc     dx,ax
  333. l2:     lodsw
  334.     adc     dx,ax
  335. l1:     lodsw
  336.     adc     dx,ax
  337. l0:     loop    deloop          ; :-)
  338.  
  339.     adc     dx,0            ; get last carries
  340.     adc     dx,0
  341.     mov     ax,dx           ; result into ax
  342.     xchg    al,ah           ; byte swap result (8088 is little-endian)
  343.     ret
  344. lcsum   endp
  345.  
  346. ; Link timer handler into timer chain
  347. ; Arg == address of timer handler routine
  348. ; MUST be called exactly once before uchtimer is called!
  349.  
  350. toff    dw      ?               ; save location for old vector
  351. tseg    dw      ?               ;  must be in code segment
  352.  
  353.     public  chtimer
  354. chtimer proc
  355.     arg     vec:far ptr
  356.     uses    ds
  357.  
  358.     mov     ah,35h          ; get current vector
  359.     mov     al,TIMEVEC
  360.     int     21h             ; puts vector in es:bx
  361.     mov     cs:tseg,es      ; stash
  362.     mov     cs:toff,bx
  363.  
  364.     mov     ah,25h
  365.     mov     al,TIMEVEC
  366.     lds     dx,vec          ; ds:si = vec
  367.  
  368.     int     21h             ; set new vector
  369.     ret
  370. chtimer endp
  371.  
  372. ; unchain timer handler from timer chain
  373. ; MUST NOT be called before chtimer!
  374.     public  uchtimer
  375. uchtimer        proc
  376.     uses    ds
  377.  
  378.     mov     ah,25h
  379.     mov     al,TIMEVEC
  380.     mov     dx,toff
  381.     mov     ds,tseg
  382.     int     21h             ; restore old vector
  383.     ret
  384. uchtimer        endp
  385.  
  386. ; Clock tick interrupt handler. Note the use of "label" rather than "proc"
  387. ; here, necessitated by the fact that "proc" automatically generates BP-saving
  388. ; code that we don't want here.
  389.  
  390.     public  btick
  391.     label   btick   far
  392.  
  393.     pushf
  394.     push    ds
  395.     cli
  396.     mov     ds,cs:dbase     ; establish interrupt data segment
  397.  
  398.     mov     Sssave,ss       ; stash user stack context
  399.     mov     Spsave,sp
  400.  
  401.     mov     ss,cs:dbase
  402.     lea     sp,Stktop
  403.  
  404.       ;  push    ax              ; save user regs on interrupt stack
  405.       ;  push    bx
  406.       ;  push    cx
  407.       ;  push    dx
  408.       ;  push    bp
  409.       ;  push    si
  410.       ;  push    di
  411.       ;  push    es
  412.     PUSHALL
  413.     push    es
  414.     call    ctick
  415.  
  416.     pop     es
  417.     POPALL
  418.       ;  pop     di
  419.       ;  pop     si
  420.       ;  pop     bp
  421.       ;  pop     dx
  422.       ;  pop     cx
  423.       ;  pop     bx
  424.       ;  pop     ax
  425.     mov     ss,Sssave
  426.     mov     sp,Spsave       ; restore original stack context
  427.     pop     ds
  428.     popf
  429.     jmp     dword ptr [toff]                ; link to previous vector
  430.  
  431. ; Convert 32-bit int in network order to host order (dh, dl, ah, al)
  432. ; Called from C as
  433. ; int32 get32(char *cp);
  434.  
  435.     public  get32
  436. get32   proc
  437.     arg     cp:ptr
  438.     if      @Datasize NE 0
  439.         uses    ds,si
  440.         lds     si,cp   ; ds:si = cp
  441.     else
  442.         uses    si
  443.         mov     si,cp   ; ds:si = cp (ds already set)
  444.     endif
  445.  
  446.     cld
  447.     lodsw
  448.     mov     dh,al   ; high word to dx, a-swapping as we go
  449.     mov     dl,ah
  450.     lodsw
  451.     xchg    al,ah   ; low word stays in ax, just swap
  452.     ret
  453. get32   endp
  454.  
  455. ; Convert 16-bit int in network order to host order (ah, al)
  456. ; Called from C as
  457. ; int16 get16(char *cp);
  458.  
  459.     public  get16
  460. get16   proc
  461.     arg     cp:ptr
  462.     if      @Datasize NE 0
  463.         uses    ds,si
  464.         lds     si,cp   ; ds:si = cp
  465.     else
  466.         uses    si
  467.         mov     si,cp   ; ds:si = cp (ds already set)
  468.     endif
  469.  
  470.     lodsw           ; note: direction flag is don't-care
  471.     xchg    al,ah   ; word stays in ax, just swap
  472.     ret
  473. get16   endp
  474.  
  475. ; Convert 32-bit int to network order, returning new pointer
  476. ; Called from C as
  477. ; char *put32(char *cp,int32 x);
  478.  
  479.     public  put32
  480. put32   proc
  481.     arg     cp:ptr,x:dword
  482.     if      @Datasize NE 0
  483.         uses    ds,di
  484.         les     di,cp   ; es:di = cp
  485.         mov     ax,ss   ; our parameter is on the stack, and ds might not
  486.         mov     ds,ax   ;   be pointing to ss.
  487.     else
  488.         uses    di
  489.         mov     di,cp   ; es:di = cp
  490.         mov     ax,ds   ; point es at data segment
  491.         mov     es,ax
  492.     endif
  493.  
  494.     cld
  495.     mov     ax,word ptr (x+2)       ; read high word of machine version
  496.     xchg    ah,al                   ; swap bytes
  497.     stosw                           ; output in network order
  498.     mov     ax,word ptr x           ; read low word of machine version
  499.     xchg    ah,al                   ; swap bytes
  500.     stosw                           ; put in network order
  501.  
  502.     mov     ax,di   ; return incremented output pointer
  503.     if      @Datasize NE 0
  504.         mov     dx,es   ; upper half of pointer
  505.     endif
  506.     ret
  507. put32   endp
  508.  
  509. ; Convert 16-bit int to network order, returning new pointer
  510. ; Called from C as
  511. ; char *put16(char *cp,int16 x);
  512.  
  513.     public  put16
  514. put16   proc
  515.     arg     cp:ptr,x:word
  516.     uses    di
  517.     if      @Datasize NE 0
  518.         les     di,cp   ;es:di = cp
  519.     else
  520.         mov     di,cp   ; es:di = cp
  521.         mov     ax,ds
  522.         mov     es,ax
  523.     endif
  524.     cld
  525.     mov     ax,x    ; fetch source word in machine order
  526.     xchg    ah,al   ; swap bytes
  527.     stosw           ; save in network order
  528.     mov     ax,di   ; return new output pointer to user
  529.     if      @Datasize NE 0
  530.         mov     dx,es   ; upper half of pointer
  531.     endif
  532.     ret
  533. put16   endp
  534.  
  535. ; kbraw - raw, nonblocking read from console
  536. ; If character is ready, return it; if not, return -1
  537.     public  kbraw
  538. kbraw   proc
  539.     mov     ah,06h  ; Direct Console I/O
  540.     mov     dl,0ffh ; Read from keyboard
  541.     int     21h     ; Call DOS
  542.     jz      @@1     ; zero flag set -> no character ready
  543.     mov     ah,0    ; valid char is 0-255
  544.     ret
  545. @@1:    mov     ax,-1   ; no char, return -1
  546.     ret
  547. kbraw   endp
  548.  
  549. if      @CPU AND 2
  550. ; fast I/O buffer routines
  551. ; version for 80186, 286, 386 (uses ins, outs instructions)
  552.  
  553. ; outbuf - put a buffer to an output port
  554.     public  outbuf
  555. outbuf  proc
  556.     arg     port:word,buf:ptr,cnt:word
  557.     if      @Datasize NE 0
  558.         uses    ds,si
  559.         lds     si,buf  ; ds:si = buf
  560.     else
  561.         uses    si
  562.         mov     si,buf  ;ds:si = buf (ds already set)
  563.     endif
  564.     mov     dx,port
  565.     mov     cx,cnt
  566.     cld
  567.     rep outsb               ; works only on PC/AT (80286)
  568.     mov     dx,ds
  569.     mov     ax,si           ; return pointer just past end of buffer
  570.     ret
  571. outbuf  endp
  572.  
  573. ; inbuf - get a buffer from an input port
  574.     public  inbuf
  575. inbuf   proc
  576.     arg     port:word,buf:ptr,cnt:word
  577.     uses    di
  578.     if      @Datasize NE 0
  579.         les     di,buf          ; es:di = buf
  580.     else
  581.         mov     di,buf          ; es:di = buf
  582.         mov     ax,ds
  583.         mov     es,ax
  584.     endif
  585.     mov     dx,port
  586.     mov     cx,cnt
  587.     cld
  588.     rep insb                ; works only on PC/AT (80286)
  589.     mov     dx,es
  590.     mov     ax,di           ; return pointer just past end of buffer
  591.     ret
  592. inbuf   endp
  593.  
  594. else
  595.  
  596. ; fast buffer I/O routines
  597. ; version for 8086/8
  598.  
  599. ; outbuf - put a buffer to an output port
  600.     public  outbuf
  601. outbuf  proc
  602.     arg     port:word,buf:ptr,cnt:word
  603.     if      @Datasize NE 0
  604.         uses    ds,si
  605.         lds     si,buf  ; ds:si = buf
  606.     else
  607.         uses    si
  608.         mov     si,buf  ; ds:si = buf (ds already set)
  609.     endif
  610.  
  611.     mov     dx,port
  612.     mov     cx,cnt
  613.     cld
  614.  
  615. ; If buffer doesn't begin on a word boundary, send the first byte
  616.     test    si,1    ; (buf & 1) ?
  617.     jz      @@even ; no
  618.     lodsb           ; al = *si++;
  619.     out     dx,al   ; out(dx,al);
  620.     dec     cx      ; cx--;
  621.     mov     cnt,cx  ; save for later test
  622. @@even:
  623.     shr     cx,1    ; cx = cnt >> 1; (convert to word count)
  624. ; Do the bulk of the buffer, a word at a time
  625.     jcxz    @@nobuf ; if(cx != 0){
  626. @@deloop:
  627.     lodsw           ; do { ax = *si++; (si is word pointer)
  628.     out     dx,al   ; out(dx,lowbyte(ax));
  629.     mov     al,ah
  630.     out     dx,al   ; out(dx,hibyte(ax));
  631.     loop    @@deloop        ; } while(--cx != 0); }
  632. ; now check for odd trailing byte
  633. @@nobuf:
  634.     mov     cx,cnt
  635.     test    cx,1
  636.     jz      @@cnteven
  637.     lodsb           ; al = *si++;
  638.     out     dx,al
  639. @@cnteven:
  640.     mov     dx,ds
  641.     mov     ax,si           ; return pointer just past end of buffer
  642.     ret
  643. outbuf  endp
  644.  
  645. ; inbuf - get a buffer from an input port
  646.     public  inbuf
  647. inbuf   proc
  648.     arg port:word,buf:ptr,cnt:word
  649.     uses    di
  650.     if      @Datasize NE 0
  651.         les     di,buf  ; es:di = buf
  652.     else
  653.         mov     di,buf  ; es:di = buf
  654.         mov     ax,ds
  655.         mov     es,ax
  656.     endif
  657.     mov     dx,port
  658.     mov     cx,cnt
  659.     cld
  660.  
  661. ; If buffer doesn't begin on a word boundary, get the first byte
  662.     test    di,1    ; if(buf & 1){
  663.     jz      @@bufeven ;
  664.     in      al,dx   ; al = in(dx);
  665.     stosb           ; *di++ = al
  666.     dec     cx      ; cx--;
  667.     mov     cnt,cx  ; cnt = cx; } save for later test
  668. @@bufeven:
  669.     shr     cx,1    ; cx = cnt >> 1; (convert to word count)
  670. ; Do the bulk of the buffer, a word at a time
  671.     jcxz    @@nobuf ; if(cx != 0){
  672. @@deloop:
  673.     in      al,dx   ; do { al = in(dx);
  674.     mov     ah,al
  675.     in      al,dx   ; ah = in(dx);
  676.     xchg    al,ah
  677.     stosw           ; *si++ = ax; (di is word pointer)
  678.     loop    @@deloop        ; } while(--cx != 0);
  679. ; now check for odd trailing byte
  680. @@nobuf:
  681.     mov     cx,cnt
  682.     test    cx,1
  683.     jz      @@cnteven
  684.     in      al,dx
  685.     stosb           ; *di++ = al
  686. @@cnteven:
  687.     mov     dx,es
  688.     mov     ax,di           ; return pointer just past end of buffer
  689.     ret
  690. inbuf   endp
  691.  
  692. endif
  693.  
  694.     public  longdiv
  695.  
  696. ; long unsigned integer division - divide an arbitrary length dividend by
  697. ; a 16-bit divisor. Replaces the dividend with the quotient and returns the
  698. ; remainder. Called from C as
  699. ;
  700. ; unsigned short
  701. ; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
  702. ;
  703. ;Register usage:
  704. ; di - divisor
  705. ; si - pointer into dividend array
  706. ; cx - loop counter, initialized to the number of 16-bit words in the dividend
  707. ; ax - low word of current dividend before each divide, current quotient after
  708. ; dx - remainder from previous divide carried over, becomes high word of
  709. ;      dividend for next divide
  710.  
  711. longdiv proc
  712.     arg     divisor:word,cnt:word,dividend:ptr
  713.     if      @Datasize NE 0
  714.         uses    ds,si,di
  715.         lds     si,dividend
  716.     else
  717.         uses    si,di
  718.         mov     si,dividend     ;si -> dividend array
  719.     endif
  720.  
  721.     cmp     divisor,0               ; divisor == 0?
  722.     jne     @2                      ; no, ok
  723.     xor     ax,ax                   ; yes, avoid divide-by-zero trap
  724.     jmp     short @1
  725.  
  726. @2:     mov     dx,0                    ; init remainder = 0
  727.     mov     cx,cnt                  ; init cnt
  728.     mov     di,divisor              ; cache divisor in register
  729.  
  730. @@deloop:
  731.     mov     ax,word ptr [si]        ; fetch current word of dividend
  732.     cmp     ax,0                    ; dividend == 0 ?
  733.     jne     @7                      ; nope, must do division
  734.     cmp     dx,0                    ; remainder also == 0?
  735.     je      @4                      ; yes, skip division, continue
  736.  
  737. @7:     div     di                      ; do division
  738.     mov     word ptr [si],ax        ; save quotient
  739.  
  740. @4:     inc     si                      ; next word of dividend
  741.     inc     si
  742.     loop    @@deloop
  743.  
  744.     mov     ax,dx                   ; return last remainder
  745. @1:     ret
  746.  
  747. longdiv endp
  748.  
  749. ; long unsigned integer multiplication - multiply an arbitrary length
  750. ; multiplicand by a 16-bit multiplier, leaving the product in place of
  751. ; the multipler, returning the carry. Called from C as
  752. ;
  753. ; unsigned short
  754. ; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
  755. ;
  756. ; Register usage:
  757. ; di = multiplier
  758. ; si = pointer to current word of multiplicand
  759. ; bx = carry from previous round
  760. ; cx = count of words in multiplicand
  761. ; dx,ax = scratch for multiply
  762.  
  763.     public longmul
  764. longmul proc    far
  765.     arg     multiplier:word,n:word,multiplicand:ptr
  766.     if      @Datasize NE 0
  767.         uses    ds,si,di
  768.         lds     si,multiplicand
  769.     else
  770.         uses    si,di
  771.         mov     si,multiplicand ; si -> multiplicand array
  772.     endif
  773.  
  774.     mov     di,multiplier           ; cache multiplier in register
  775.     xor     bx,bx                   ; init carry = 0
  776.     mov     cx,n                    ; fetch n
  777.     mov     ax,cx
  778.     shl     ax,1                    ; *2 = word offset
  779.     add     si,ax                   ; multiplicand += n
  780.  
  781. @@deloop:
  782.     dec     si
  783.     dec     si                      ; work from right to left
  784.     mov     ax,word ptr [si]        ; fetch current multiplicand
  785.     or      ax,ax                   ; skip multiply if zero
  786.     jz      @@nomult
  787.     mul     di                      ; dx:ax <- ax * di
  788. @@nomult:
  789.     add     ax,bx                   ; add carry from previous multiply
  790.     mov     word ptr [si],ax        ; save low order word of product
  791.     mov     bx,0                    ; clear previous carry, leaving CF alone
  792.     adc     bx,dx                   ; save new carry
  793.     xor     dx,dx                   ; clear in case we skip the next mult
  794.     loop    @@deloop
  795.  
  796.     mov     ax,bx                   ; return final carry
  797.     ret
  798. longmul endp
  799.  
  800. ifdef   notdef
  801. ; divide 32 bits by 16 bits, returning both quotient and remainder
  802. ; This allows C programs that need both to avoid having to do two divisions
  803. ;
  804. ; Called from C as
  805. ;       long divrem(dividend,divisor)
  806. ;       long dividend;
  807. ;       short divisor;
  808. ;
  809. ;       The quotient is returned in the low 16 bits of the result,
  810. ;       and the remainder is returned in the high 16 bits.
  811.  
  812.     public  divrem
  813. divrem  proc
  814.     arg     dividend:dword,divisor:word
  815.     mov     ax,word ptr dividend
  816.     mov     dx,word ptr (dividend+2)
  817.     div     divisor
  818.     ret
  819. divrem  endp
  820. endif   
  821.  
  822. ; General purpose hash function for IP addresses
  823. ; Uses lookup table Hashtab[] initialized in ip.c
  824. ; Called from C as
  825. ; char hash_ip(int32 ipaddr);
  826.  
  827.     public hash_ip
  828. hash_ip proc
  829.     arg     ipaddr:dword
  830.     lea     bx,Hashtab
  831.     mov     ax,word ptr ipaddr
  832.     xor     ax,word ptr (ipaddr+2)
  833.     xor     al,ah
  834.     xlat
  835.     xor     ah,ah
  836.     ret
  837. hash_ip endp
  838.  
  839. ; Compute int(log2(x))
  840. ; Called from C as
  841. ; int log2(int16 x);
  842.  
  843.     public  log2
  844. log2    proc
  845.     arg     x:word
  846.     mov     cx,16
  847.     mov     ax,x
  848. @@2:    rcl     ax,1 
  849.     jc      @@1
  850.     loop    @@2
  851. @@1:    dec     cx
  852.     mov     ax,cx
  853.     ret
  854. log2    endp
  855.     end
  856.